The following warnings are resolved in 1.0.25, but the lateset release is 1.0.26.
warning: 'kIOMasterPortDefault' is deprecated: first deprecated in macOS 12.0 [-Wdeprecated-declarations]
mac/libusb/os/darwin_usb.c
mac/libusb/os/events_posix.c
mac/libusb/os/threads_posix.c
- mac/libusb/hotplug.h
mac/libusb/libusb.h
mac/libusb/libusbi.h
mac/libusb/version.h
mac/libusb/XCode/config.h
)
target_include_directories(usb-1.0 PRIVATE mac/libusb/XCode PUBLIC mac/libusb)
- target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation")
+ target_link_libraries(usb-1.0 INTERFACE objc "-framework IOKit" "-framework CoreFoundation" "-framework Security")
list(APPEND LIBS usb-1.0)
elseif(GPSBABEL_WITH_LIBUSB STREQUAL "custom")
message(STATUS "libusb-1.0 is enabled but but must be manually configured.")
# TODO: It would be better to create an archive and link to it
# to separate library build requirements from gpsbabel requirements.
DEFINES += LIBUSB_H_INCLUDE=$$shell_quote(\"mac/libusb/libusb.h\")
- LIBS += -lobjc -framework IOKit -framework CoreFoundation
+ LIBS += -lobjc -framework IOKit -framework CoreFoundation -framework Security
INCLUDEPATH += mac/libusb \
mac/libusb/Xcode
SOURCES += \
mac/libusb/os/events_posix.c \
mac/libusb/os/threads_posix.c
HEADERS += \
- mac/libusb/hotplug.h \
mac/libusb/libusb.h \
mac/libusb/libusbi.h \
mac/libusb/version.h \
-This is libusb-1.0.24 from https://libusb.info/.
+This is libusb-1.0.26 from https://libusb.info/.
Since we have such problems with people
getting libusb successfully built - between the Universal Build issues
and the fact that we have to work hard to go find where it's installed
*/
#include "libusbi.h"
-#include "hotplug.h"
#include "version.h"
#ifdef __ANDROID__
#include <syslog.h>
#endif
-struct libusb_context *usbi_default_context;
static const struct libusb_version libusb_version_internal =
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
LIBUSB_RC, "http://libusb.info" };
-static int default_context_refcnt;
-static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
static struct timespec timestamp_origin;
#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
static libusb_log_cb log_handler;
#endif
+struct libusb_context *usbi_default_context;
+struct libusb_context *usbi_fallback_context;
+static int default_context_refcnt;
+static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct usbi_option default_context_options[LIBUSB_OPTION_MAX];
+
+
usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
struct list_head active_contexts_list;
* - libusb is able to send a packet of zero length to an endpoint simply by
* submitting a transfer of zero length.
* - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET
- * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux.
+ * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux,
+ * Darwin and Windows (WinUSB).
*/
/**
*
* The libusb_get_device_list() function can be used to obtain a list of
* devices currently connected to the system. This is known as device
- * discovery.
+ * discovery. Devices can also be discovered with the hotplug mechanism,
+ * whereby a callback function registered with libusb_hotplug_register_callback()
+ * will be called when a device of interest is connected or disconnected.
*
* Just because you have a reference to a device does not mean it is
* necessarily usable. The device may have been unplugged, you may not have
*
* With the above information in mind, the process of opening a device can
* be viewed as follows:
- * -# Discover devices using libusb_get_device_list().
+ * -# Discover devices using libusb_get_device_list() or libusb_hotplug_register_callback().
* -# Choose the device that you want to operate, and call libusb_open().
* -# Unref all devices in the discovered device list.
* -# Free the discovered device list.
* which grows when required. it can be freed once discovery has completed,
* eliminating the need for a list node in the libusb_device structure
* itself. */
-#define DISCOVERED_DEVICES_SIZE_STEP 8
+#define DISCOVERED_DEVICES_SIZE_STEP 16
static struct discovered_devs *discovered_devs_alloc(void)
{
}
/* exceeded capacity, need to grow */
- usbi_dbg("need to increase capacity");
+ usbi_dbg(DEVICE_CTX(dev), "need to increase capacity");
capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
/* can't use usbi_reallocf here because in failure cases it would
* free the existing discdevs without unreferencing its devices. */
if (!dev)
return NULL;
- usbi_mutex_init(&dev->lock);
+ usbi_atomic_store(&dev->refcnt, 1);
dev->ctx = ctx;
- dev->refcnt = 1;
dev->session_data = session_id;
dev->speed = LIBUSB_SPEED_UNKNOWN;
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
- usbi_connect_device (dev);
- }
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ usbi_connect_device(dev);
return dev;
}
{
struct libusb_context *ctx = DEVICE_CTX(dev);
- dev->attached = 1;
+ usbi_atomic_store(&dev->attached, 1);
usbi_mutex_lock(&dev->ctx->usb_devs_lock);
list_add(&dev->list, &dev->ctx->usb_devs);
usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
- /* Signal that an event has occurred for this device if we support hotplug AND
- * the hotplug message list is ready. This prevents an event from getting raised
- * during initial enumeration. */
- if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
- usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
- }
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
}
void usbi_disconnect_device(struct libusb_device *dev)
{
struct libusb_context *ctx = DEVICE_CTX(dev);
- usbi_mutex_lock(&dev->lock);
- dev->attached = 0;
- usbi_mutex_unlock(&dev->lock);
+ usbi_atomic_store(&dev->attached, 0);
usbi_mutex_lock(&ctx->usb_devs_lock);
list_del(&dev->list);
usbi_mutex_unlock(&ctx->usb_devs_lock);
- /* Signal that an event has occurred for this device if we support hotplug AND
- * the hotplug message list is ready. This prevents an event from getting raised
- * during initial enumeration. libusb_handle_events will take care of dereferencing
- * the device. */
- if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
- usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
- }
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
}
/* Perform some final sanity checks on a newly discovered device. If this
usbi_err(DEVICE_CTX(dev), "too many configurations");
return LIBUSB_ERROR_IO;
} else if (0 == num_configurations) {
- usbi_dbg("zero configurations, maybe an unauthorized device");
+ usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device");
}
return 0;
int r = 0;
ssize_t i, len;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
if (!discdevs)
return LIBUSB_ERROR_NO_MEM;
DEFAULT_VISIBILITY
libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
{
- usbi_mutex_lock(&dev->lock);
- dev->refcnt++;
- usbi_mutex_unlock(&dev->lock);
+ long refcnt;
+
+ refcnt = usbi_atomic_inc(&dev->refcnt);
+ assert(refcnt >= 2);
+
return dev;
}
*/
void API_EXPORTED libusb_unref_device(libusb_device *dev)
{
- int refcnt;
+ long refcnt;
if (!dev)
return;
- usbi_mutex_lock(&dev->lock);
- refcnt = --dev->refcnt;
- usbi_mutex_unlock(&dev->lock);
+ refcnt = usbi_atomic_dec(&dev->refcnt);
+ assert(refcnt >= 0);
if (refcnt == 0) {
- usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+ usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address);
libusb_unref_device(dev->parent_dev);
usbi_disconnect_device(dev);
}
- usbi_mutex_destroy(&dev->lock);
free(dev);
}
}
* handle for the underlying device. The handle allows you to use libusb to
* perform I/O on the device in question.
*
- * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY)
- * before libusb_init if don't have authority to access the usb device directly.
+ * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before
+ * libusb_init() if you want to skip enumeration of USB devices. In particular,
+ * this might be needed on Android if you don't have authority to access USB
+ * devices in general.
*
* On Linux, the system device handle must be a valid file descriptor opened
* on the device node.
*
* This is a non-blocking function; no requests are sent over the bus.
*
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \param ctx the context to operate on, or NULL for the default context
* \param sys_dev the platform-specific system device handle
* \param dev_handle output location for the returned device handle pointer. Only
size_t priv_size = usbi_backend.device_handle_priv_size;
int r;
- usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
+ usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
ctx = usbi_get_context(ctx);
r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev);
if (r < 0) {
- usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
+ usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
usbi_mutex_destroy(&_dev_handle->lock);
free(_dev_handle);
return r;
struct libusb_device_handle *_dev_handle;
size_t priv_size = usbi_backend.device_handle_priv_size;
int r;
- usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
- if (!dev->attached) {
+ usbi_dbg(DEVICE_CTX(dev), "open %d.%d", dev->bus_number, dev->device_address);
+
+ if (!usbi_atomic_load(&dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
- }
_dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
if (!_dev_handle)
r = usbi_backend.open(_dev_handle);
if (r < 0) {
- usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
+ usbi_dbg(DEVICE_CTX(dev), "open %d.%d returns %d", dev->bus_number, dev->device_address, r);
libusb_unref_device(dev);
usbi_mutex_destroy(&_dev_handle->lock);
free(_dev_handle);
* just making sure that we don't attempt to process the transfer after
* the device handle is invalid
*/
- usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed",
+ usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed",
transfer, dev_handle);
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (!dev_handle)
return;
- usbi_dbg(" ");
-
ctx = HANDLE_CTX(dev_handle);
+ usbi_dbg(ctx, " ");
+
handling_events = usbi_handling_events(ctx);
/* Similarly to libusb_open(), we want to interrupt all event handlers
{
int r = LIBUSB_ERROR_NOT_SUPPORTED;
uint8_t tmp = 0;
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
if (usbi_backend.get_configuration)
r = usbi_backend.get_configuration(dev_handle, &tmp);
if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
- usbi_dbg("falling back to control message");
+ usbi_dbg(ctx, "falling back to control message");
r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
if (r == 1) {
r = 0;
} else if (r == 0) {
- usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
+ usbi_err(ctx, "zero bytes returned in ctrl transfer?");
r = LIBUSB_ERROR_IO;
} else {
- usbi_dbg("control failed, error %d", r);
+ usbi_dbg(ctx, "control failed, error %d", r);
}
}
if (r == 0) {
- usbi_dbg("active config %u", tmp);
+ usbi_dbg(ctx, "active config %u", tmp);
*config = (int)tmp;
}
int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
int configuration)
{
- usbi_dbg("configuration %d", configuration);
+ usbi_dbg(HANDLE_CTX(dev_handle), "configuration %d", configuration);
if (configuration < -1 || configuration > (int)UINT8_MAX)
return LIBUSB_ERROR_INVALID_PARAM;
return usbi_backend.set_configuration(dev_handle, configuration);
{
int r = 0;
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
usbi_mutex_lock(&dev_handle->lock);
{
int r;
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
int interface_number, int alternate_setting)
{
- usbi_dbg("interface %d altsetting %d",
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d altsetting %d",
interface_number, alternate_setting);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX)
return LIBUSB_ERROR_INVALID_PARAM;
- usbi_mutex_lock(&dev_handle->lock);
- if (!dev_handle->dev->attached) {
+ if (!usbi_atomic_load(&dev_handle->dev->attached)) {
usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NO_DEVICE;
}
+ usbi_mutex_lock(&dev_handle->lock);
if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NOT_FOUND;
int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
unsigned char endpoint)
{
- usbi_dbg("endpoint %x", endpoint);
- if (!dev_handle->dev->attached)
+ usbi_dbg(HANDLE_CTX(dev_handle), "endpoint 0x%x", endpoint);
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
return usbi_backend.clear_halt(dev_handle, endpoint);
*/
int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
{
- usbi_dbg(" ");
- if (!dev_handle->dev->attached)
+ usbi_dbg(HANDLE_CTX(dev_handle), " ");
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.reset_device)
int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
{
- usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+ usbi_dbg(HANDLE_CTX(dev_handle), "streams %u eps %d", (unsigned)num_streams, num_endpoints);
if (!num_streams || !endpoints || num_endpoints <= 0)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.alloc_streams)
int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
unsigned char *endpoints, int num_endpoints)
{
- usbi_dbg("eps %d", num_endpoints);
+ usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints);
if (!endpoints || num_endpoints <= 0)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.free_streams)
unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
size_t length)
{
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return NULL;
if (usbi_backend.dev_mem_alloc)
int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.kernel_driver_active)
* Detach a kernel driver from an interface. If successful, you will then be
* able to claim the interface and perform I/O.
*
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
*
* Note that libusb itself also talks to the device through a special kernel
* driver, if this driver is already attached to the device, this call will
int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.detach_kernel_driver)
/** \ingroup libusb_dev
* Re-attach an interface's kernel driver, which was previously detached
- * using libusb_detach_kernel_driver(). This call is only effective on
- * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ * using libusb_detach_kernel_driver().
*
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
*
* \param dev_handle a device handle
* \param interface_number the interface to attach the driver from
int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.attach_kernel_driver)
* If ENABLE_DEBUG_LOGGING is defined then per context callback function will
* never be called.
*
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \param ctx context on which to assign log handler, or NULL for the default
* context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested.
* \param cb pointer to the callback function, or NULL to stop log
* Some options require one or more arguments to be provided. Consult each
* option's documentation for specific requirements.
*
+ * If the context ctx is NULL, the option will be added to a list of default
+ * options that will be applied to all subsequently created contexts.
+ *
* Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106
*
* \param ctx context on which to operate
int API_EXPORTED libusb_set_option(libusb_context *ctx,
enum libusb_option option, ...)
{
- int arg, r = LIBUSB_SUCCESS;
+ int arg = 0, r = LIBUSB_SUCCESS;
va_list ap;
- ctx = usbi_get_context(ctx);
-
va_start(ap, option);
- switch (option) {
- case LIBUSB_OPTION_LOG_LEVEL:
+ if (LIBUSB_OPTION_LOG_LEVEL == option) {
arg = va_arg(ap, int);
if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
r = LIBUSB_ERROR_INVALID_PARAM;
- break;
}
+ }
+ va_end(ap);
+
+ if (LIBUSB_SUCCESS != r) {
+ return r;
+ }
+
+ if (option >= LIBUSB_OPTION_MAX) {
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (NULL == ctx) {
+ usbi_mutex_static_lock(&default_context_lock);
+ default_context_options[option].is_set = 1;
+ if (LIBUSB_OPTION_LOG_LEVEL == option) {
+ default_context_options[option].arg.ival = arg;
+ }
+ usbi_mutex_static_unlock(&default_context_lock);
+ }
+
+ ctx = usbi_get_context(ctx);
+ if (NULL == ctx) {
+ return LIBUSB_SUCCESS;
+ }
+
+ switch (option) {
+ case LIBUSB_OPTION_LOG_LEVEL:
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
if (!ctx->debug_fixed)
ctx->debug = (enum libusb_log_level)arg;
#endif
break;
- /* Handle all backend-specific options here */
+ /* Handle all backend-specific options here */
case LIBUSB_OPTION_USE_USBDK:
- case LIBUSB_OPTION_WEAK_AUTHORITY:
+ case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
if (usbi_backend.set_option)
- r = usbi_backend.set_option(ctx, option, ap);
- else
- r = LIBUSB_ERROR_NOT_SUPPORTED;
+ return usbi_backend.set_option(ctx, option, ap);
+
+ return LIBUSB_ERROR_NOT_SUPPORTED;
break;
+ case LIBUSB_OPTION_MAX:
default:
- r = LIBUSB_ERROR_INVALID_PARAM;
+ return LIBUSB_ERROR_INVALID_PARAM;
}
- va_end(ap);
- return r;
+ return LIBUSB_SUCCESS;;
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
* context will be created. If there was already a default context, it will
* be reused (and nothing will be initialized/reinitialized).
*
- * \param context Optional output location for context pointer.
+ * \param ctx Optional output location for context pointer.
* Only valid on return code 0.
* \returns 0 on success, or a LIBUSB_ERROR code on failure
* \see libusb_contexts
*/
-int API_EXPORTED libusb_init(libusb_context **context)
+int API_EXPORTED libusb_init(libusb_context **ctx)
{
- struct libusb_device *dev, *next;
size_t priv_size = usbi_backend.context_priv_size;
- struct libusb_context *ctx;
- static int first_init = 1;
- int r = 0;
+ struct libusb_context *_ctx;
+ int r;
usbi_mutex_static_lock(&default_context_lock);
- if (!timestamp_origin.tv_sec)
- usbi_get_monotonic_time(×tamp_origin);
-
- if (!context && usbi_default_context) {
- usbi_dbg("reusing default context");
+ if (!ctx && default_context_refcnt > 0) {
+ usbi_dbg(usbi_default_context, "reusing default context");
default_context_refcnt++;
usbi_mutex_static_unlock(&default_context_lock);
return 0;
}
- ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
- if (!ctx) {
- r = LIBUSB_ERROR_NO_MEM;
- goto err_unlock;
+ /* check for first init */
+ if (!active_contexts_list.next) {
+ list_init(&active_contexts_list);
+ usbi_get_monotonic_time(×tamp_origin);
+ }
+
+ _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size);
+ if (!_ctx) {
+ usbi_mutex_static_unlock(&default_context_lock);
+ return LIBUSB_ERROR_NO_MEM;
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- ctx->debug = get_env_debug_level();
- if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
- ctx->debug_fixed = 1;
+ if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
+ _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
+ } else {
+ _ctx->debug = get_env_debug_level();
+ }
+ if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
+ _ctx->debug_fixed = 1;
#endif
- /* default context should be initialized before calling usbi_dbg */
- if (!usbi_default_context) {
- usbi_default_context = ctx;
- default_context_refcnt++;
- usbi_dbg("created default context");
+ usbi_mutex_init(&_ctx->usb_devs_lock);
+ usbi_mutex_init(&_ctx->open_devs_lock);
+ list_init(&_ctx->usb_devs);
+ list_init(&_ctx->open_devs);
+
+ /* apply default options to all new contexts */
+ for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) {
+ if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) {
+ continue;
+ }
+ r = libusb_set_option(_ctx, option);
+ if (LIBUSB_SUCCESS != r)
+ goto err_free_ctx;
+ }
+
+ /* default context must be initialized before calling usbi_dbg */
+ if (!ctx) {
+ usbi_default_context = _ctx;
+ default_context_refcnt = 1;
+ usbi_dbg(usbi_default_context, "created default context");
}
- usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
+ usbi_dbg(_ctx, "libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc);
- usbi_mutex_init(&ctx->usb_devs_lock);
- usbi_mutex_init(&ctx->open_devs_lock);
- usbi_mutex_init(&ctx->hotplug_cbs_lock);
- list_init(&ctx->usb_devs);
- list_init(&ctx->open_devs);
- list_init(&ctx->hotplug_cbs);
- ctx->next_hotplug_cb_handle = 1;
+ r = usbi_io_init(_ctx);
+ if (r < 0)
+ goto err_free_ctx;
usbi_mutex_static_lock(&active_contexts_lock);
- if (first_init) {
- first_init = 0;
- list_init(&active_contexts_list);
- }
- list_add (&ctx->list, &active_contexts_list);
+ list_add(&_ctx->list, &active_contexts_list);
usbi_mutex_static_unlock(&active_contexts_lock);
if (usbi_backend.init) {
- r = usbi_backend.init(ctx);
+ r = usbi_backend.init(_ctx);
if (r)
- goto err_free_ctx;
+ goto err_io_exit;
}
- r = usbi_io_init(ctx);
- if (r < 0)
- goto err_backend_exit;
+ /* Initialize hotplug after the initial enumeration is done. */
+ usbi_hotplug_init(_ctx);
- usbi_mutex_static_unlock(&default_context_lock);
+ if (ctx) {
+ *ctx = _ctx;
- if (context)
- *context = ctx;
+ if (!usbi_fallback_context) {
+ usbi_fallback_context = _ctx;
+ usbi_warn(usbi_fallback_context, "installing new context as implicit default");
+ }
+ }
- return 0;
+ usbi_mutex_static_unlock(&default_context_lock);
-err_backend_exit:
- if (usbi_backend.exit)
- usbi_backend.exit(ctx);
-err_free_ctx:
- if (ctx == usbi_default_context) {
- usbi_default_context = NULL;
- default_context_refcnt--;
- }
+ return 0;
+err_io_exit:
usbi_mutex_static_lock(&active_contexts_lock);
- list_del(&ctx->list);
+ list_del(&_ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
- usbi_mutex_lock(&ctx->usb_devs_lock);
- for_each_device_safe(ctx, dev, next) {
- list_del(&dev->list);
- libusb_unref_device(dev);
+ usbi_hotplug_exit(_ctx);
+ usbi_io_exit(_ctx);
+
+err_free_ctx:
+ if (!ctx) {
+ /* clear default context that was not fully initialized */
+ usbi_default_context = NULL;
+ default_context_refcnt = 0;
}
- usbi_mutex_unlock(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->open_devs_lock);
- usbi_mutex_destroy(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+ usbi_mutex_destroy(&_ctx->open_devs_lock);
+ usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+ free(_ctx);
- free(ctx);
-err_unlock:
usbi_mutex_static_unlock(&default_context_lock);
+
return r;
}
*/
void API_EXPORTED libusb_exit(libusb_context *ctx)
{
- struct libusb_device *dev, *next;
- struct timeval tv = { 0, 0 };
- int destroying_default_context = 0;
-
- usbi_dbg(" ");
+ struct libusb_context *_ctx;
+ struct libusb_device *dev;
- ctx = usbi_get_context(ctx);
+ usbi_mutex_static_lock(&default_context_lock);
/* if working with default context, only actually do the deinitialization
* if we're the last user */
- usbi_mutex_static_lock(&default_context_lock);
- if (ctx == usbi_default_context) {
+ if (!ctx) {
if (!usbi_default_context) {
- usbi_dbg("no default context, not initialized?");
+ usbi_dbg(ctx, "no default context, not initialized?");
usbi_mutex_static_unlock(&default_context_lock);
return;
}
if (--default_context_refcnt > 0) {
- usbi_dbg("not destroying default context");
+ usbi_dbg(ctx, "not destroying default context");
usbi_mutex_static_unlock(&default_context_lock);
return;
}
- usbi_dbg("destroying default context");
- /*
- * Setting this flag without unlocking the default context, as
- * we are actually destroying the default context.
- * usbi_default_context is not set to NULL yet, as all activities
- * would only stop after usbi_backend->exit() returns.
- */
- destroying_default_context = 1;
+ usbi_dbg(ctx, "destroying default context");
+ _ctx = usbi_default_context;
} else {
- /* Unlock default context, as we're not modifying it. */
- usbi_mutex_static_unlock(&default_context_lock);
+ usbi_dbg(ctx, " ");
+ _ctx = ctx;
}
usbi_mutex_static_lock(&active_contexts_lock);
- list_del(&ctx->list);
+ list_del(&_ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
- if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
- usbi_hotplug_deregister(ctx, 1);
-
- /*
- * Ensure any pending unplug events are read from the hotplug
- * pipe. The usb_device-s hold in the events are no longer part
- * of usb_devs, but the events still hold a reference!
- *
- * Note we don't do this if the application has left devices
- * open (which implies a buggy app) to avoid packet completion
- * handlers running when the app does not expect them to run.
- */
- if (list_empty(&ctx->open_devs))
- libusb_handle_events_timeout(ctx, &tv);
+ if (usbi_backend.exit)
+ usbi_backend.exit(_ctx);
- usbi_mutex_lock(&ctx->usb_devs_lock);
- for_each_device_safe(ctx, dev, next) {
- list_del(&dev->list);
- libusb_unref_device(dev);
- }
- usbi_mutex_unlock(&ctx->usb_devs_lock);
- }
+ if (!ctx)
+ usbi_default_context = NULL;
+ if (ctx == usbi_fallback_context)
+ usbi_fallback_context = NULL;
- /* a few sanity checks. don't bother with locking because unless
- * there is an application bug, nobody will be accessing these. */
- if (!list_empty(&ctx->usb_devs))
- usbi_warn(ctx, "some libusb_devices were leaked");
- if (!list_empty(&ctx->open_devs))
- usbi_warn(ctx, "application left some devices open");
+ usbi_mutex_static_unlock(&default_context_lock);
- usbi_io_exit(ctx);
- if (usbi_backend.exit)
- usbi_backend.exit(ctx);
+ /* Don't bother with locking after this point because unless there is
+ * an application bug, nobody will be accessing the context. */
- usbi_mutex_destroy(&ctx->open_devs_lock);
- usbi_mutex_destroy(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
- free(ctx);
+ usbi_hotplug_exit(_ctx);
+ usbi_io_exit(_ctx);
- if (destroying_default_context) {
- usbi_default_context = NULL;
- usbi_mutex_static_unlock(&default_context_lock);
+ for_each_device(_ctx, dev) {
+ usbi_warn(_ctx, "device %d.%d still referenced",
+ dev->bus_number, dev->device_address);
+ DEVICE_CTX(dev) = NULL;
}
+
+ if (!list_empty(&_ctx->open_devs))
+ usbi_warn(_ctx, "application left some devices open");
+
+ usbi_mutex_destroy(&_ctx->open_devs_lock);
+ usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+ free(_ctx);
}
/** \ingroup libusb_misc
#else
enum libusb_log_level ctx_level;
- ctx = usbi_get_context(ctx);
+ ctx = ctx ? ctx : usbi_default_context;
+ ctx = ctx ? ctx : usbi_fallback_context;
if (ctx)
ctx_level = ctx->debug;
else
header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
buffer += header->bLength;
size -= header->bLength;
parsed += header->bLength;
header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
buffer += header->bLength;
size -= header->bLength;
}
int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc)
{
- usbi_dbg(" ");
+ usbi_dbg(DEVICE_CTX(dev), " ");
static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE,
"struct libusb_device_descriptor is not expected size");
*desc = dev->device_descriptor;
uint8_t *buf;
int r;
- usbi_dbg("index %u", config_index);
+ usbi_dbg(DEVICE_CTX(dev), "index %u", config_index);
if (config_index >= dev->device_descriptor.bNumConfigurations)
return LIBUSB_ERROR_NOT_FOUND;
return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
}
- usbi_dbg("value %u", bConfigurationValue);
+ usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue);
for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
union usbi_config_desc_buf _config;
uint16_t bos_len;
uint8_t *bos_data;
int r;
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
/* Read the BOS. This generates 2 requests on the bus,
* one for the header, and one for the full BOS */
r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf));
if (r < 0) {
if (r != LIBUSB_ERROR_PIPE)
- usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ usbi_err(ctx, "failed to read BOS (%d)", r);
return r;
}
if (r < LIBUSB_DT_BOS_SIZE) {
- usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d",
+ usbi_err(ctx, "short BOS read %d/%d",
r, LIBUSB_DT_BOS_SIZE);
return LIBUSB_ERROR_IO;
}
bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength);
- usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities",
+ usbi_dbg(ctx, "found BOS descriptor: size %u bytes, %u capabilities",
bos_len, _bos.desc.bNumDeviceCaps);
bos_data = calloc(1, bos_len);
if (!bos_data)
r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len);
if (r >= 0) {
if (r != (int)bos_len)
- usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u",
- r, bos_len);
+ usbi_warn(ctx, "short BOS read %d/%u", r, bos_len);
r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r);
} else {
- usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ usbi_err(ctx, "failed to read BOS (%d)", r);
}
free(bos_data);
else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
return LIBUSB_ERROR_IO;
else if (str.desc.bLength & 1)
- usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength);
langid = libusb_le16_to_cpu(str.desc.wData[0]);
r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf));
else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
return LIBUSB_ERROR_IO;
else if ((str.desc.bLength & 1) || str.desc.bLength != r)
- usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r);
di = 0;
for (si = 2; si < str.desc.bLength; si += 2) {
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
/*
* Hotplug functions for libusb
- * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2021 Nathan Hjelm <hjelmn@mac.com>
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
*
* This library is free software; you can redistribute it and/or
*/
#include "libusbi.h"
-#include "hotplug.h"
/**
* @defgroup libusb_hotplug Device hotplug event notification
* A hotplug event can listen for either or both of these events.
*
* Note: If you receive notification that a device has left and you have any
- * a libusb_device_handles for the device it is up to you to call libusb_close()
+ * libusb_device_handles for the device it is up to you to call libusb_close()
* on each device handle to free up any remaining resources associated with the device.
* Once a device has left any libusb_device_handle associated with the device
* are invalid and will remain so even if the device comes back.
*/
#define VALID_HOTPLUG_EVENTS \
- (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
- LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
#define VALID_HOTPLUG_FLAGS \
- (LIBUSB_HOTPLUG_ENUMERATE)
+ (LIBUSB_HOTPLUG_ENUMERATE)
-static int usbi_hotplug_match_cb(struct libusb_context *ctx,
- struct libusb_device *dev, libusb_hotplug_event event,
- struct libusb_hotplug_callback *hotplug_cb)
+void usbi_hotplug_init(struct libusb_context *ctx)
+{
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ return;
+
+ usbi_mutex_init(&ctx->hotplug_cbs_lock);
+ list_init(&ctx->hotplug_cbs);
+ ctx->next_hotplug_cb_handle = 1;
+ usbi_atomic_store(&ctx->hotplug_ready, 1);
+}
+
+void usbi_hotplug_exit(struct libusb_context *ctx)
+{
+ struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+ struct usbi_hotplug_message *msg;
+ struct libusb_device *dev, *next_dev;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ return;
+
+ if (!usbi_atomic_load(&ctx->hotplug_ready))
+ return;
+
+ /* free all registered hotplug callbacks */
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+
+ /* free all pending hotplug messages */
+ while (!list_empty(&ctx->hotplug_msgs)) {
+ msg = list_first_entry(&ctx->hotplug_msgs, struct usbi_hotplug_message, list);
+
+ /* if the device left, the message holds a reference
+ * and we must drop it */
+ if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ libusb_unref_device(msg->device);
+
+ list_del(&msg->list);
+ free(msg);
+ }
+
+ /* free all discovered devices. due to parent references loop until no devices are freed. */
+ for_each_device_safe(ctx, dev, next_dev) {
+ /* remove the device from the usb_devs list only if there are no
+ * references held, otherwise leave it on the list so that a
+ * warning message will be shown */
+ if (usbi_atomic_load(&dev->refcnt) == 1) {
+ list_del(&dev->list);
+ }
+ if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
+ /* the parent was before this device in the list and will be released.
+ remove it from the list. this is safe as parent_dev can not be
+ equal to next_dev. */
+ assert (dev->parent_dev != next_dev);
+ list_del(&dev->parent_dev->list);
+ }
+ libusb_unref_device(dev);
+ }
+
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+}
+
+static int usbi_hotplug_match_cb(struct libusb_device *dev,
+ libusb_hotplug_event event, struct usbi_hotplug_callback *hotplug_cb)
{
if (!(hotplug_cb->flags & event)) {
return 0;
return 0;
}
- return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data);
-}
-
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event)
-{
- struct libusb_hotplug_callback *hotplug_cb, *next;
- int ret;
-
- usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-
- for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
- if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
- /* process deregistration in usbi_hotplug_deregister() */
- continue;
- }
-
- usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
- ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb);
- usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-
- if (ret) {
- list_del(&hotplug_cb->list);
- free(hotplug_cb);
- }
- }
-
- usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+ return hotplug_cb->cb(DEVICE_CTX(dev), dev, event, hotplug_cb->user_data);
}
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event)
{
- struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+ struct usbi_hotplug_message *msg;
unsigned int event_flags;
- if (!message) {
+ /* Only generate a notification if hotplug is ready. This prevents hotplug
+ * notifications from being generated during initial enumeration or if the
+ * backend does not support hotplug. */
+ if (!usbi_atomic_load(&ctx->hotplug_ready))
+ return;
+
+ msg = calloc(1, sizeof(*msg));
+ if (!msg) {
usbi_err(ctx, "error allocating hotplug message");
return;
}
- message->event = event;
- message->device = dev;
+ msg->event = event;
+ msg->device = dev;
/* Take the event data lock and add this message to the list.
* Only signal an event if there are no prior pending events. */
usbi_mutex_lock(&ctx->event_data_lock);
event_flags = ctx->event_flags;
ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
- list_add_tail(&message->list, &ctx->hotplug_msgs);
+ list_add_tail(&msg->list, &ctx->hotplug_msgs);
if (!event_flags)
usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs)
+{
+ struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+ struct usbi_hotplug_message *msg;
+ int r;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ /* dispatch all pending hotplug messages */
+ while (!list_empty(hotplug_msgs)) {
+ msg = list_first_entry(hotplug_msgs, struct usbi_hotplug_message, list);
+
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ /* skip callbacks that have unregistered */
+ if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)
+ continue;
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+ r = usbi_hotplug_match_cb(msg->device, msg->event, hotplug_cb);
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ if (r) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ /* if the device left, the message holds a reference
+ * and we must drop it */
+ if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ libusb_unref_device(msg->device);
+
+ list_del(&msg->list);
+ free(msg);
+ }
+
+ /* free any callbacks that have unregistered */
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
+ usbi_dbg(ctx, "freeing hotplug cb %p with handle %d",
+ hotplug_cb, hotplug_cb->handle);
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+}
+
int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
int events, int flags,
int vendor_id, int product_id, int dev_class,
libusb_hotplug_callback_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle)
{
- struct libusb_hotplug_callback *new_callback;
+ struct usbi_hotplug_callback *hotplug_cb;
/* check for sane values */
- if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
+ if (!events || (~VALID_HOTPLUG_EVENTS & events) ||
(~VALID_HOTPLUG_FLAGS & flags) ||
(LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
}
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return LIBUSB_ERROR_NOT_SUPPORTED;
- }
ctx = usbi_get_context(ctx);
- new_callback = calloc(1, sizeof(*new_callback));
- if (!new_callback) {
+ hotplug_cb = calloc(1, sizeof(*hotplug_cb));
+ if (!hotplug_cb)
return LIBUSB_ERROR_NO_MEM;
- }
- new_callback->flags = (uint8_t)events;
+ hotplug_cb->flags = (uint8_t)events;
if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
- new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
- new_callback->vendor_id = (uint16_t)vendor_id;
+ hotplug_cb->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
+ hotplug_cb->vendor_id = (uint16_t)vendor_id;
}
if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
- new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
- new_callback->product_id = (uint16_t)product_id;
+ hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
+ hotplug_cb->product_id = (uint16_t)product_id;
}
if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
- new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
- new_callback->dev_class = (uint8_t)dev_class;
+ hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
+ hotplug_cb->dev_class = (uint8_t)dev_class;
}
- new_callback->cb = cb_fn;
- new_callback->user_data = user_data;
+ hotplug_cb->cb = cb_fn;
+ hotplug_cb->user_data = user_data;
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
/* protect the handle by the context hotplug lock */
- new_callback->handle = ctx->next_hotplug_cb_handle++;
+ hotplug_cb->handle = ctx->next_hotplug_cb_handle++;
/* handle the unlikely case of overflow */
if (ctx->next_hotplug_cb_handle < 0)
ctx->next_hotplug_cb_handle = 1;
- list_add(&new_callback->list, &ctx->hotplug_cbs);
+ list_add(&hotplug_cb->list, &ctx->hotplug_cbs);
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
- usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
+ usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle);
if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
ssize_t i, len;
len = libusb_get_device_list(ctx, &devs);
if (len < 0) {
- libusb_hotplug_deregister_callback(ctx,
- new_callback->handle);
+ libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle);
return (int)len;
}
for (i = 0; i < len; i++) {
- usbi_hotplug_match_cb(ctx, devs[i],
+ usbi_hotplug_match_cb(devs[i],
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
- new_callback);
+ hotplug_cb);
}
libusb_free_device_list(devs, 1);
}
-
if (callback_handle)
- *callback_handle = new_callback->handle;
+ *callback_handle = hotplug_cb->handle;
return LIBUSB_SUCCESS;
}
void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle)
{
- struct libusb_hotplug_callback *hotplug_cb;
+ struct usbi_hotplug_callback *hotplug_cb;
int deregistered = 0;
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return;
- }
- usbi_dbg("deregister hotplug cb %d", callback_handle);
+ usbi_dbg(ctx, "deregister hotplug cb %d", callback_handle);
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
for_each_hotplug_cb(ctx, hotplug_cb) {
if (callback_handle == hotplug_cb->handle) {
- /* Mark this callback for deregistration */
+ /* mark this callback for deregistration */
hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
deregistered = 1;
+ break;
}
}
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle)
{
- struct libusb_hotplug_callback *hotplug_cb;
+ struct usbi_hotplug_callback *hotplug_cb;
void *user_data = NULL;
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return NULL;
- }
- usbi_dbg("get hotplug user data %d", callback_handle);
+ usbi_dbg(ctx, "get hotplug cb %d user data", callback_handle);
ctx = usbi_get_context(ctx);
for_each_hotplug_cb(ctx, hotplug_cb) {
if (callback_handle == hotplug_cb->handle) {
user_data = hotplug_cb->user_data;
+ break;
}
}
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
return user_data;
}
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
-{
- struct libusb_hotplug_callback *hotplug_cb, *next;
-
- usbi_mutex_lock(&ctx->hotplug_cbs_lock);
- for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
- if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
- usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
- hotplug_cb->handle);
- list_del(&hotplug_cb->list);
- free(hotplug_cb);
- }
- }
- usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-}
+++ /dev/null
-/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
-/*
- * Hotplug support for libusb
- * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
- * Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef USBI_HOTPLUG_H
-#define USBI_HOTPLUG_H
-
-#include "libusbi.h"
-
-enum usbi_hotplug_flags {
- /* This callback is interested in device arrivals */
- USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
-
- /* This callback is interested in device removals */
- USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
-
- /* IMPORTANT: The values for the below entries must start *after*
- * the highest value of the above entries!!!
- */
-
- /* The vendor_id field is valid for matching */
- USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
-
- /* The product_id field is valid for matching */
- USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
-
- /* The dev_class field is valid for matching */
- USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
-
- /* This callback has been unregistered and needs to be freed */
- USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
-};
-
-/** \ingroup hotplug
- * The hotplug callback structure. The user populates this structure with
- * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
- * to receive notification of hotplug events.
- */
-struct libusb_hotplug_callback {
- /** Flags that control how this callback behaves */
- uint8_t flags;
-
- /** Vendor ID to match (if flags says this is valid) */
- uint16_t vendor_id;
-
- /** Product ID to match (if flags says this is valid) */
- uint16_t product_id;
-
- /** Device class to match (if flags says this is valid) */
- uint8_t dev_class;
-
- /** Callback function to invoke for matching event/device */
- libusb_hotplug_callback_fn cb;
-
- /** Handle for this callback (used to match on deregister) */
- libusb_hotplug_callback_handle handle;
-
- /** User data that will be passed to the callback function */
- void *user_data;
-
- /** List this callback is registered in (ctx->hotplug_cbs) */
- struct list_head list;
-};
-
-struct libusb_hotplug_message {
- /** The hotplug event that occurred */
- libusb_hotplug_event event;
-
- /** The device for which this hotplug event occurred */
- struct libusb_device *device;
-
- /** List this message is contained in (ctx->hotplug_msgs) */
- struct list_head list;
-};
-
-#define for_each_hotplug_cb(ctx, c) \
- for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-#define for_each_hotplug_cb_safe(ctx, c, n) \
- for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
-void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
-
-#endif
*/
#include "libusbi.h"
-#include "hotplug.h"
/**
* \page libusb_io Synchronous and asynchronous device I/O
* a single function call. When the function call returns, the transfer has
* completed and you can parse the results.
*
- * If you have used the libusb-0.1 before, this I/O style will seem familiar to
+ * If you have used libusb-0.1 before, this I/O style will seem familiar to
* you. libusb-0.1 only offered a synchronous interface.
*
* In our input device example, to read button presses you might write code
#ifdef HAVE_OS_TIMER
r = usbi_create_timer(&ctx->timer);
if (r == 0) {
- usbi_dbg("using timer for timeouts");
+ usbi_dbg(ctx, "using timer for timeouts");
r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
if (r < 0)
goto err_destroy_timer;
} else {
- usbi_dbg("timer not available for timeouts");
+ usbi_dbg(ctx, "timer not available for timeouts");
}
#endif
itransfer->priv = ptr;
usbi_mutex_init(&itransfer->lock);
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- usbi_dbg("transfer %p", transfer);
return transfer;
}
if (!transfer)
return;
- usbi_dbg("transfer %p", transfer);
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer);
if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
free(transfer->buffer);
itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
usbi_mutex_destroy(&itransfer->lock);
+ if (itransfer->dev)
+ libusb_unref_device(itransfer->dev);
priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
ptr = (unsigned char *)itransfer - priv_size;
/* act on first transfer that has not already been handled */
if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
- usbi_dbg("no timeouts, disarming timer");
+ usbi_dbg(ctx, "no timeouts, disarming timer");
return usbi_disarm_timer(&ctx->timer);
}
#else
if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
/* if this transfer has the lowest timeout of all active transfers,
* rearm the timer with this transfer's timeout */
- usbi_dbg("arm timer for timeout in %ums (first in line)",
+ usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
r = usbi_arm_timer(&ctx->timer, timeout);
}
{
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
- struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ struct libusb_context *ctx;
int r;
- usbi_dbg("transfer %p", transfer);
+ assert(transfer->dev_handle);
+ if (itransfer->dev)
+ libusb_unref_device(itransfer->dev);
+ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev);
+
+ ctx = HANDLE_CTX(transfer->dev_handle);
+ usbi_dbg(ctx, "transfer %p", transfer);
/*
* Important note on locking, this function takes / releases locks
}
/*
* We must release the flying transfers lock here, because with
- * some backends the submit_transfer method is synchroneous.
+ * some backends the submit_transfer method is synchronous.
*/
usbi_mutex_unlock(&ctx->flying_transfers_lock);
r = usbi_backend.submit_transfer(itransfer);
if (r == LIBUSB_SUCCESS) {
itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
- /* keep a reference to this device */
- libusb_ref_device(transfer->dev_handle->dev);
}
usbi_mutex_unlock(&itransfer->lock);
* \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
* "LIBUSB_TRANSFER_CANCELLED."
*
+ * This function behaves differently on Darwin-based systems (macOS and iOS):
+ *
+ * - Calling this function for one transfer will cause all transfers on the
+ * same endpoint to be cancelled. Your callback function will be invoked with
+ * a transfer status of
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
+ * "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled.
+
+ * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request
+ * for the transfer's endpoint. If the device does not handle this request
+ * correctly, the data toggle bits for the endpoint can be left out of sync
+ * between host and device, which can have unpredictable results when the
+ * next data is sent on the endpoint, including data being silently lost.
+ * A call to \ref libusb_clear_halt will not resolve this situation, since
+ * that function uses the same request. Therefore, if your program runs on
+ * Darwin and uses a device that does not correctly implement
+ * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel
+ * transfers when followed by a device reset using
+ * \ref libusb_reset_device.
+ *
* \param transfer the transfer to cancel
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
{
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
int r;
- usbi_dbg("transfer %p", transfer );
+ usbi_dbg(ctx, "transfer %p", transfer );
usbi_mutex_lock(&itransfer->lock);
if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
|| (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
if (r < 0) {
if (r != LIBUSB_ERROR_NOT_FOUND &&
r != LIBUSB_ERROR_NO_DEVICE)
- usbi_err(TRANSFER_CTX(transfer),
- "cancel transfer failed error %d", r);
+ usbi_err(ctx, "cancel transfer failed error %d", r);
else
- usbi_dbg("cancel transfer failed error %d", r);
+ usbi_dbg(ctx, "cancel transfer failed error %d", r);
if (r == LIBUSB_ERROR_NO_DEVICE)
itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
{
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct libusb_device_handle *dev_handle = transfer->dev_handle;
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
uint8_t flags;
int r;
r = remove_from_flying_list(itransfer);
if (r < 0)
- usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
+ usbi_err(ctx, "failed to set timer for next timeout");
usbi_mutex_lock(&itransfer->lock);
itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
if (rqlen != itransfer->transferred) {
- usbi_dbg("interpreting short transfer as error");
+ usbi_dbg(ctx, "interpreting short transfer as error");
status = LIBUSB_TRANSFER_ERROR;
}
}
flags = transfer->flags;
transfer->status = status;
transfer->actual_length = itransfer->transferred;
- usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+ usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback);
if (transfer->callback)
transfer->callback(transfer);
/* transfer might have been freed by the above call, do not use from
* this point. */
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
libusb_free_transfer(transfer);
- libusb_unref_device(dev_handle->dev);
return r;
}
/* if the URB was cancelled due to timeout, report timeout to the user */
if (timed_out) {
- usbi_dbg("detected timeout cancellation");
+ usbi_dbg(ctx, "detected timeout cancellation");
return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
}
* function will be called the next time an event handler runs. */
void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
{
- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle;
+ struct libusb_device *dev = itransfer->dev;
- if (dev_handle) {
- struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+ if (dev) {
+ struct libusb_context *ctx = DEVICE_CTX(dev);
unsigned int event_flags;
usbi_mutex_lock(&ctx->event_data_lock);
ru = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (ru) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 0;
}
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
{
unsigned int event_flags;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
static int handle_event_trigger(struct libusb_context *ctx)
{
struct list_head hotplug_msgs;
+ int hotplug_event = 0;
int r = 0;
- usbi_dbg("event triggered");
+ usbi_dbg(ctx, "event triggered");
list_init(&hotplug_msgs);
/* check if someone modified the event sources */
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
- usbi_dbg("someone updated the event sources");
+ usbi_dbg(ctx, "someone updated the event sources");
if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
- usbi_dbg("someone purposefully interrupted");
+ usbi_dbg(ctx, "someone purposefully interrupted");
ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
}
+ if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
+ usbi_dbg(ctx, "someone unregistered a hotplug cb");
+ ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+ hotplug_event = 1;
+ }
+
/* check if someone is closing a device */
if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
- usbi_dbg("someone is closing a device");
+ usbi_dbg(ctx, "someone is closing a device");
/* check for any pending hotplug messages */
if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
- usbi_dbg("hotplug message received");
+ usbi_dbg(ctx, "hotplug message received");
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+ hotplug_event = 1;
assert(!list_empty(&ctx->hotplug_msgs));
list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
}
usbi_mutex_unlock(&ctx->event_data_lock);
- /* process the hotplug messages, if any */
- while (!list_empty(&hotplug_msgs)) {
- struct libusb_hotplug_message *message =
- list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
- usbi_hotplug_match(ctx, message->device, message->event);
-
- /* the device left, dereference the device */
- if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
- libusb_unref_device(message->device);
-
- list_del(&message->list);
- free(message);
- }
+ /* process the hotplug events, if any */
+ if (hotplug_event)
+ usbi_hotplug_process(ctx, &hotplug_msgs);
return r;
}
* save the additional overhead */
usbi_mutex_lock(&ctx->event_data_lock);
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
- usbi_dbg("event sources modified, reallocating event data");
+ usbi_dbg(ctx, "event sources modified, reallocating event data");
/* free anything removed since we last ran */
cleanup_removed_event_sources(ctx);
if (libusb_try_lock_events(ctx) == 0) {
if (completed == NULL || !*completed) {
/* we obtained the event lock: do our own event handling */
- usbi_dbg("doing our own event handling");
+ usbi_dbg(ctx, "doing our own event handling");
r = handle_events(ctx, &poll_timeout);
}
libusb_unlock_events(ctx);
/* we hit a race: whoever was event handling earlier finished in the
* time it took us to reach this point. try the cycle again. */
libusb_unlock_event_waiters(ctx);
- usbi_dbg("event handler was active but went away, retrying");
+ usbi_dbg(ctx, "event handler was active but went away, retrying");
goto retry;
}
- usbi_dbg("another thread is doing event handling");
+ usbi_dbg(ctx, "another thread is doing event handling");
r = libusb_wait_for_event(ctx, &poll_timeout);
already_done:
usbi_mutex_lock(&ctx->flying_transfers_lock);
if (list_empty(&ctx->flying_transfers)) {
usbi_mutex_unlock(&ctx->flying_transfers_lock);
- usbi_dbg("no URBs, no timeout!");
+ usbi_dbg(ctx, "no URBs, no timeout!");
return 0;
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (!TIMESPEC_IS_SET(&next_timeout)) {
- usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+ usbi_dbg(ctx, "no URB with timeout or all handled by OS; no timeout!");
return 0;
}
usbi_get_monotonic_time(&systime);
if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
- usbi_dbg("first timeout already expired");
+ usbi_dbg(ctx, "first timeout already expired");
timerclear(tv);
} else {
TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
- usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
+ usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
}
return 1;
if (!ievent_source)
return LIBUSB_ERROR_NO_MEM;
- usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+ usbi_dbg(ctx, "add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
ievent_source->data.os_handle = os_handle;
ievent_source->data.poll_events = poll_events;
usbi_mutex_lock(&ctx->event_data_lock);
struct usbi_event_source *ievent_source;
int found = 0;
- usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
+ usbi_dbg(ctx, "remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
usbi_mutex_lock(&ctx->event_data_lock);
for_each_event_source(ctx, ievent_source) {
if (ievent_source->data.os_handle == os_handle) {
}
if (!found) {
- usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
+ usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
usbi_mutex_unlock(&ctx->event_data_lock);
return;
}
struct usbi_transfer *cur;
struct usbi_transfer *to_cancel;
- usbi_dbg("device %d.%d",
+ usbi_dbg(ctx, "device %d.%d",
dev_handle->dev->bus_number, dev_handle->dev->device_address);
/* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
if (!to_cancel)
break;
- usbi_dbg("cancelling transfer %p from disconnect",
+ usbi_dbg(ctx, "cancelling transfer %p from disconnect",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
usbi_mutex_lock(&to_cancel->lock);
#define LIBUSB_H
#if defined(_MSC_VER)
+#pragma warning(push)
+/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */
+#pragma warning(disable:4200)
/* on MS environments, the inline keyword is available in C++ only */
#if !defined(__cplusplus)
#define inline __inline
#endif
/* ssize_t is also not available */
+#ifndef _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED
#include <basetsd.h>
typedef SSIZE_T ssize_t;
+#endif /* _SSIZE_T_DEFINED */
#endif /* _MSC_VER */
#include <limits.h>
* Internally, LIBUSB_API_VERSION is defined as follows:
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
*/
-#define LIBUSB_API_VERSION 0x01000108
+#define LIBUSB_API_VERSION 0x01000109
/* The following is kept for compatibility, but will be deprecated in the future */
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
/** \ingroup libusb_asyncio
* Setup packet for control transfers. */
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
#pragma pack(push, 1)
#endif
struct libusb_control_setup {
/** Number of bytes to transfer */
uint16_t wLength;
} LIBUSB_PACKED;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
#pragma pack(pop)
#endif
* Sessions are created by libusb_init() and destroyed through libusb_exit().
* If your application is guaranteed to only ever include a single libusb
* user (i.e. you), you do not have to worry about contexts: pass NULL in
- * every function call where a context is required. The default context
- * will be used.
+ * every function call where a context is required, and the default context
+ * will be used. Note that libusb_set_option(NULL, ...) is special, and adds
+ * an option to a list of default options for new contexts.
*
* For more information, see \ref libusb_contexts.
*/
/** \ingroup libusb_dev
* Structure representing a USB device detected on the system. This is an
* opaque type for which you are only ever provided with a pointer, usually
- * originating from libusb_get_device_list().
+ * originating from libusb_get_device_list() or libusb_hotplug_register_callback().
*
* Certain operations can be performed on a device, but in order to do any
* I/O you will have to first obtain a device handle using libusb_open().
/** \ingroup libusb_lib
* Log callback mode.
+ *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \see libusb_set_log_cb()
*/
enum libusb_log_cb_mode {
* is a global log message
* \param level the log level, see \ref libusb_log_level for a description
* \param str the log message
+ *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \see libusb_set_log_cb()
*/
typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
*/
LIBUSB_OPTION_USE_USBDK = 1,
- /** Set libusb has weak authority. With this option, libusb will skip
- * scan devices in libusb_init.
+ /** Do not scan for devices
+ *
+ * With this option set, libusb will skip scanning devices in
+ * libusb_init(). Must be set before calling libusb_init().
*
- * This option should be set before calling libusb_init(), otherwise
- * libusb_init will failed. Normally libusb_wrap_sys_device need set
- * this option.
+ * Hotplug functionality will also be deactivated.
*
- * Only valid on Linux-based operating system, such as Android.
+ * The option is useful in combination with libusb_wrap_sys_device(),
+ * which can access a device directly without prior device scanning.
+ *
+ * This is typically needed on Android, where access to USB devices
+ * is limited.
+ *
+ * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY
+ *
+ * Only valid on Linux.
*/
- LIBUSB_OPTION_WEAK_AUTHORITY = 2
+ LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2,
+
+#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
+
+ LIBUSB_OPTION_MAX = 3
};
int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#if defined(__cplusplus)
}
#endif
os/events_posix.c \
os/threads_posix.c
-HEADERS = hotplug.h \
- libusb.h \
+HEADERS = libusb.h \
libusbi.h \
version.h \
version_nano.h \
#define PTR_ALIGN(v) \
(((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
+/* Atomic operations
+ *
+ * Useful for reference counting or when accessing a value without a lock
+ *
+ * The following atomic operations are defined:
+ * usbi_atomic_load() - Atomically read a variable's value
+ * usbi_atomic_store() - Atomically write a new value value to a variable
+ * usbi_atomic_inc() - Atomically increment a variable's value and return the new value
+ * usbi_atomic_dec() - Atomically decrement a variable's value and return the new value
+ *
+ * All of these operations are ordered with each other, thus the effects of
+ * any one operation is guaranteed to be seen by any other operation.
+ */
+#ifdef _MSC_VER
+typedef volatile LONG usbi_atomic_t;
+#define usbi_atomic_load(a) (*(a))
+#define usbi_atomic_store(a, v) (*(a)) = (v)
+#define usbi_atomic_inc(a) InterlockedIncrement((a))
+#define usbi_atomic_dec(a) InterlockedDecrement((a))
+#else
+#include <stdatomic.h>
+typedef atomic_long usbi_atomic_t;
+#define usbi_atomic_load(a) atomic_load((a))
+#define usbi_atomic_store(a, v) atomic_store((a), (v))
+#define usbi_atomic_inc(a) (atomic_fetch_add((a), 1) + 1)
+#define usbi_atomic_dec(a) (atomic_fetch_add((a), -1) - 1)
+#endif
+
/* Internal abstractions for event handling and thread synchronization */
#if defined(PLATFORM_POSIX)
#include "os/events_posix.h"
#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define usbi_dbg(ctx ,...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
#else /* ENABLE_LOGGING */
#define usbi_err(ctx, ...) UNUSED(ctx)
#define usbi_warn(ctx, ...) UNUSED(ctx)
#define usbi_info(ctx, ...) UNUSED(ctx)
-#define usbi_dbg(...) do {} while (0)
+#define usbi_dbg(ctx, ...) do {} while (0)
#endif /* ENABLE_LOGGING */
#define DEVICE_CTX(dev) ((dev)->ctx)
-#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
+#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL)
#define ITRANSFER_CTX(itransfer) \
- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)))
+ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL)
+#define TRANSFER_CTX(transfer) \
+ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)))
#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN))
#define IS_EPOUT(ep) (!IS_EPIN(ep))
libusb_hotplug_callback_handle next_hotplug_cb_handle;
usbi_mutex_t hotplug_cbs_lock;
+ /* A flag to indicate that the context is ready for hotplug notifications */
+ usbi_atomic_t hotplug_ready;
+
/* this is a list of in-flight transfer handles, sorted by timeout
* expiration. URBs to timeout the soonest are placed at the beginning of
* the list, URBs that will time out later are placed after, and urbs with
};
extern struct libusb_context *usbi_default_context;
+extern struct libusb_context *usbi_fallback_context;
extern struct list_head active_contexts_list;
extern usbi_mutex_static_t active_contexts_lock;
static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
{
- return ctx ? ctx : usbi_default_context;
+ static int warned = 0;
+
+ if (!ctx) {
+ ctx = usbi_default_context;
+ }
+ if (!ctx) {
+ ctx = usbi_fallback_context;
+ if (ctx && warned == 0) {
+ usbi_err(ctx, "API misuse! Using non-default context as implicit default.");
+ warned = 1;
+ }
+ }
+ return ctx;
}
enum usbi_event_flags {
}
struct libusb_device {
- /* lock protects refcnt, everything else is finalized at initialization
- * time */
- usbi_mutex_t lock;
- int refcnt;
+ usbi_atomic_t refcnt;
struct libusb_context *ctx;
struct libusb_device *parent_dev;
unsigned long session_data;
struct libusb_device_descriptor device_descriptor;
- int attached;
+ usbi_atomic_t attached;
};
struct libusb_device_handle {
uint32_t state_flags; /* Protected by usbi_transfer->lock */
uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
+ /* The device reference is held until destruction for logging
+ * even after dev_handle is set to NULL. */
+ struct libusb_device *dev;
+
/* this lock is held during libusb_submit_transfer() and
* libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
* cancellation, submission-during-cancellation, etc). the OS backend
uint16_t align; /* Force 2-byte alignment */
};
+enum usbi_hotplug_flags {
+ /* This callback is interested in device arrivals */
+ USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+
+ /* This callback is interested in device removals */
+ USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+
+ /* IMPORTANT: The values for the below entries must start *after*
+ * the highest value of the above entries!!!
+ */
+
+ /* The vendor_id field is valid for matching */
+ USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
+
+ /* The product_id field is valid for matching */
+ USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
+
+ /* The dev_class field is valid for matching */
+ USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
+
+ /* This callback has been unregistered and needs to be freed */
+ USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
+};
+
+struct usbi_hotplug_callback {
+ /* Flags that control how this callback behaves */
+ uint8_t flags;
+
+ /* Vendor ID to match (if flags says this is valid) */
+ uint16_t vendor_id;
+
+ /* Product ID to match (if flags says this is valid) */
+ uint16_t product_id;
+
+ /* Device class to match (if flags says this is valid) */
+ uint8_t dev_class;
+
+ /* Callback function to invoke for matching event/device */
+ libusb_hotplug_callback_fn cb;
+
+ /* Handle for this callback (used to match on deregister) */
+ libusb_hotplug_callback_handle handle;
+
+ /* User data that will be passed to the callback function */
+ void *user_data;
+
+ /* List this callback is registered in (ctx->hotplug_cbs) */
+ struct list_head list;
+};
+
+struct usbi_hotplug_message {
+ /* The hotplug event that occurred */
+ libusb_hotplug_event event;
+
+ /* The device for which this hotplug event occurred */
+ struct libusb_device *device;
+
+ /* List this message is contained in (ctx->hotplug_msgs) */
+ struct list_head list;
+};
+
/* shared data and functions */
+void usbi_hotplug_init(struct libusb_context *ctx);
+void usbi_hotplug_exit(struct libusb_context *ctx);
+void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event);
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs);
+
int usbi_io_init(struct libusb_context *ctx);
void usbi_io_exit(struct libusb_context *ctx);
short poll_events);
void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
+struct usbi_option {
+ int is_set;
+ union {
+ int ival;
+ } arg;
+};
+
/* OS event abstraction */
int usbi_create_event(usbi_event_t *event);
* data structures for later, etc.
*
* This function is called when a libusb user initializes the library
- * prior to use.
+ * prior to use. Mutual exclusion with other init and exit calls is
+ * guaranteed when this function is called.
*
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
* that was set up by init.
*
* This function is called when the user deinitializes the library.
+ * Mutual exclusion with other init and exit calls is guaranteed when
+ * this function is called.
*/
void (*exit)(struct libusb_context *ctx);
#define for_each_removed_event_source_safe(ctx, e, n) \
for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
+#define for_each_hotplug_cb(ctx, c) \
+ for_each_helper(c, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
+#define for_each_hotplug_cb_safe(ctx, c, n) \
+ for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
#ifdef __cplusplus
}
#endif
/* -*- Mode: C; indent-tabs-mode:nil -*- */
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2020 Nathan Hjelm <hjelmn@cs.unm.edu>
- * Copyright © 2019-2020 Google LLC. All rights reserved.
+ * Copyright © 2008-2021 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2021 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* function. Its use is also conditionalized to only older deployment targets. */
#define OBJC_SILENCE_GC_DEPRECATIONS 1
+/* Default timeout to 10s for reenumerate. This is needed because USBDeviceReEnumerate
+ * does not return error status on macOS. */
+#define DARWIN_REENUMERATE_TIMEOUT_US (10 * USEC_PER_SEC)
+
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
#include <objc/objc-auto.h>
#include "darwin_usb.h"
-static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static int init_count = 0;
+/* Both kIOMasterPortDefault or kIOMainPortDefault are synonyms for 0. */
+static const mach_port_t darwin_default_master_port = 0;
+
/* async event thread */
static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
+static int darwin_reenumerate_device(struct libusb_device_handle *dev_handle, bool capture);
+static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface);
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
UInt64 old_session_id);
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
UInt64 *old_session_id);
#if defined(ENABLE_LOGGING)
case kIOReturnExclusiveAccess:
return "another process has device opened for exclusive access";
case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+ case kUSBHostReturnPipeStalled:
+#endif
return "pipe is stalled";
case kIOReturnError:
return "could not establish a connection to the Darwin kernel";
case kIOReturnExclusiveAccess:
return LIBUSB_ERROR_ACCESS;
case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+ case kUSBHostReturnPipeStalled:
+#endif
return LIBUSB_ERROR_PIPE;
case kIOReturnBadArgument:
return LIBUSB_ERROR_INVALID_PARAM;
case kIOUSBTransactionTimeout:
return LIBUSB_ERROR_TIMEOUT;
+ case kIOUSBUnknownPipeErr:
+ return LIBUSB_ERROR_NOT_FOUND;
case kIOReturnNotResponding:
case kIOReturnAborted:
case kIOReturnError:
case kIOUSBNoAsyncPortErr:
- case kIOUSBUnknownPipeErr:
default:
return LIBUSB_ERROR_OTHER;
}
(*(cached_dev->device))->Release(cached_dev->device);
cached_dev->device = NULL;
}
+ IOObjectRelease (cached_dev->service);
free (cached_dev);
}
}
uint8_t i, iface;
- usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+
+ usbi_dbg (ctx, "converting ep address 0x%02x to pipeRef and interface", ep);
for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
cInterface = &priv->interfaces[iface];
if (interface_out)
*interface_out = cInterface;
- usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+ usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface);
return LIBUSB_SUCCESS;
}
}
CFRelease (locationCF);
}
- return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
+ return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator);
}
/* Returns 1 on success, 0 on failure. */
return success;
}
-static usb_device_t **darwin_device_from_service (io_service_t service)
+static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service)
{
io_cf_plugin_ref_t *plugInInterface = NULL;
usb_device_t **device;
break;
}
- usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
+ usbi_dbg (ctx, "set up plugin for service retry: %s", darwin_error_str (kresult));
/* sleep for a little while before trying again */
nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
}
if (kIOReturnSuccess != kresult || !plugInInterface) {
- usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
+ usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult));
return NULL;
}
usbi_mutex_lock(&active_contexts_lock);
while ((service = IOIteratorNext(add_devices))) {
- ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id);
if (ret < 0 || !cached_device->can_enumerate) {
continue;
}
}
if (cached_device->in_reenumerate) {
- usbi_dbg ("cached device in reset state. reset complete...");
+ usbi_dbg (NULL, "cached device in reset state. reset complete...");
cached_device->in_reenumerate = false;
}
struct darwin_cached_device *old_device;
io_service_t device;
- UInt64 session;
+ UInt64 session, locationID;
int ret;
usbi_mutex_lock(&active_contexts_lock);
/* get the location from the i/o registry */
ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
IOObjectRelease (device);
if (!ret)
continue;
if (old_device->in_reenumerate) {
/* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
* will deref if needed. */
- usbi_dbg ("detected device detached due to re-enumeration");
+ usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64,
+ session, locationID);
/* the device object is no longer usable so go ahead and release it */
if (old_device->device) {
}
for_each_context(ctx) {
- usbi_dbg ("notifying context %p of device disconnect", ctx);
+ usbi_dbg (ctx, "notifying context %p of device disconnect", ctx);
dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
if (dev) {
/* since a kernel thread may notify the IOIterators used for
* hotplug notification we can't just clear the iterators.
* instead just wait until all IOService providers are quiet */
- (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
+ (void) IOKitWaitQuiet (darwin_default_master_port, &timeout);
}
static void darwin_clear_iterator (io_iterator_t iter) {
io_iterator_t libusb_rem_device_iterator;
io_iterator_t libusb_add_device_iterator;
- usbi_dbg ("creating hotplug event source");
+ /* ctx must only be used for logging during thread startup */
+ usbi_dbg (ctx, "creating hotplug event source");
runloop = CFRunLoopGetCurrent ();
CFRetain (runloop);
CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
/* add the notification port to the run loop */
- libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault);
+ libusb_notification_port = IONotificationPortCreate (darwin_default_master_port);
libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
IOServiceMatching(darwin_device_class),
darwin_devices_detached,
- ctx, &libusb_rem_device_iterator);
+ NULL, &libusb_rem_device_iterator);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
IOServiceMatching(darwin_device_class),
darwin_devices_attached,
- ctx, &libusb_add_device_iterator);
+ NULL, &libusb_add_device_iterator);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
darwin_clear_iterator (libusb_rem_device_iterator);
darwin_clear_iterator (libusb_add_device_iterator);
- usbi_dbg ("darwin event thread ready to receive events");
+ usbi_dbg (ctx, "darwin event thread ready to receive events");
/* signal the main thread that the hotplug runloop has been created. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
/* run the runloop */
CFRunLoopRun();
- usbi_dbg ("darwin event thread exiting");
+ usbi_dbg (NULL, "darwin event thread exiting");
/* signal the main thread that the hotplug runloop has finished. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
darwin_deref_cached_device(dev);
}
-
- darwin_cached_devices.prev = darwin_cached_devices.next = NULL;
}
static int darwin_init(struct libusb_context *ctx) {
bool first_init;
int rc;
- pthread_mutex_lock (&libusb_darwin_init_mutex);
-
first_init = (1 == ++init_count);
do {
if (first_init) {
- assert (NULL == darwin_cached_devices.next);
- list_init (&darwin_cached_devices);
-
+ if (NULL == darwin_cached_devices.next) {
+ list_init (&darwin_cached_devices);
+ }
+ assert(list_empty(&darwin_cached_devices));
#if !defined(HAVE_CLOCK_GETTIME)
/* create the clocks that will be used if clock_gettime() is not available */
host_name_port_t host_self;
--init_count;
}
- pthread_mutex_unlock (&libusb_darwin_init_mutex);
-
return rc;
}
static void darwin_exit (struct libusb_context *ctx) {
UNUSED(ctx);
- pthread_mutex_lock (&libusb_darwin_init_mutex);
-
if (0 == --init_count) {
/* stop the event runloop and wait for the thread to terminate. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
mach_port_deallocate(mach_task_self(), clock_monotonic);
#endif
}
-
- pthread_mutex_unlock (&libusb_darwin_init_mutex);
}
static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
not usable anyway */
if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) &&
0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) {
- usbi_dbg ("ignoring configuration on root hub simulation");
+ usbi_dbg (ctx, "ignoring configuration on root hub simulation");
dev->active_config = 0;
return LIBUSB_SUCCESS;
}
/* not configured */
dev->active_config = 0;
- usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
+ usbi_dbg (ctx, "active config: %u, first config: %u", dev->active_config, dev->first_config);
return LIBUSB_SUCCESS;
}
return (*device)->DeviceRequestTO (device, &req);
}
-static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) {
+static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
usb_device_t **device = dev->device;
int retries = 1;
long delay = 30000; // microseconds
0 == dev->dev_descriptor.bcdUSB)) {
/* work around for incorrectly configured devices */
if (try_reconfigure && is_open) {
- usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
+ usbi_dbg(ctx, "descriptor appears to be invalid. resetting configuration before trying again...");
/* set the first configuration */
(*device)->SetConfiguration(device, 1);
if (kIOReturnSuccess != ret2) {
/* prevent log spew from poorly behaving devices. this indicates the
os actually had trouble communicating with the device */
- usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
+ usbi_dbg(ctx, "could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
} else
unsuspended = 1;
}
if (kIOReturnSuccess != ret) {
- usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
+ usbi_dbg(ctx, "kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
/* sleep for a little while before trying again */
nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL);
}
if (ret != kIOReturnSuccess) {
/* a debug message was already printed out for this error */
if (LIBUSB_CLASS_HUB == bDeviceClass)
- usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ usbi_dbg (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
else
- usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
return darwin_to_libusb (ret);
}
return LIBUSB_ERROR_NO_DEVICE;
}
- usbi_dbg ("cached device descriptor:");
- usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
- usbi_dbg (" bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
- usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
- usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
- usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
- usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
- usbi_dbg (" idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
- usbi_dbg (" idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
- usbi_dbg (" bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
- usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
- usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct);
- usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
- usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
+ usbi_dbg (ctx, "cached device descriptor:");
+ usbi_dbg (ctx, " bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
+ usbi_dbg (ctx, " bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
+ usbi_dbg (ctx, " bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
+ usbi_dbg (ctx, " bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
+ usbi_dbg (ctx, " bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
+ usbi_dbg (ctx, " bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+ usbi_dbg (ctx, " idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
+ usbi_dbg (ctx, " idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ usbi_dbg (ctx, " bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
+ usbi_dbg (ctx, " iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
+ usbi_dbg (ctx, " iProduct: 0x%02x", dev->dev_descriptor.iProduct);
+ usbi_dbg (ctx, " iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
+ usbi_dbg (ctx, " bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
dev->can_enumerate = 1;
return false;
}
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
UInt64 *old_session_id) {
struct darwin_cached_device *new_device;
UInt64 sessionID = 0, parent_sessionID = 0;
(void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
(void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
if (!get_device_port (service, &port)) {
- usbi_dbg("could not get connected port number");
+ usbi_dbg(ctx, "could not get connected port number");
}
- usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
+ usbi_dbg(ctx, "finding cached device for sessionID 0x%" PRIx64, sessionID);
if (get_device_parent_sessionID(service, &parent_sessionID)) {
- usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
+ usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID);
}
usbi_mutex_lock(&darwin_cached_devices_lock);
do {
list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
- usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+ usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
sessionID, locationID, new_device->session, new_device->location);
if (new_device->location == locationID && new_device->in_reenumerate) {
- usbi_dbg ("found cached device with matching location that is being re-enumerated");
+ usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated");
*old_session_id = new_device->session;
break;
}
if (new_device->session == sessionID) {
- usbi_dbg("using cached device for device");
+ usbi_dbg(ctx, "using cached device for device");
*cached_out = new_device;
break;
}
if (*cached_out)
break;
- usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+ usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID);
- device = darwin_device_from_service (service);
+ device = darwin_device_from_service (ctx, service);
if (!device) {
ret = LIBUSB_ERROR_NO_DEVICE;
break;
(*device)->GetLocationID (device, &new_device->location);
new_device->port = port;
new_device->parent_session = parent_sessionID;
+ } else {
+ /* release the ref to old device's service */
+ IOObjectRelease (new_device->service);
}
/* keep track of devices regardless of if we successfully enumerate them to
new_device->session = sessionID;
new_device->device = device;
+ new_device->service = service;
+
+ /* retain the service */
+ IOObjectRetain (service);
/* cache the device descriptor */
- ret = darwin_cache_device_descriptor(new_device);
+ ret = darwin_cache_device_descriptor(ctx, new_device);
if (ret)
break;
break;
if (0 != old_session_id) {
- usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
+ usbi_dbg (ctx, "re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
ctx, old_session_id, cached_device->session);
/* save the libusb device before the session id is updated */
dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id);
}
if (!dev) {
- usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ usbi_dbg (ctx, "allocating new device in context %p for with session 0x%" PRIx64,
ctx, cached_device->session);
dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
priv->dev = cached_device;
darwin_ref_cached_device (priv->dev);
dev->port_number = cached_device->port;
+ /* the location ID encodes the path to the device. the top byte of the location ID contains the bus number
+ (numbered from 0). the remaining bytes can be used to construct the device tree for that bus. */
dev->bus_number = cached_device->location >> 24;
assert(cached_device->address <= UINT8_MAX);
dev->device_address = (uint8_t)cached_device->address;
usbi_localize_device_descriptor(&dev->device_descriptor);
dev->session_data = cached_device->session;
+ if (NULL != dev->parent_dev) {
+ libusb_unref_device(dev->parent_dev);
+ dev->parent_dev = NULL;
+ }
+
if (cached_device->parent_session > 0) {
dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
- } else {
- dev->parent_dev = NULL;
}
(*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
if (ret < 0)
break;
- usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+ usbi_dbg (ctx, "found device with address %d port = %d parent = %p at %p", dev->device_address,
dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
} while (0);
return darwin_to_libusb (kresult);
while ((service = IOIteratorNext (deviceIterator))) {
- ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ ret = darwin_get_cached_device (ctx, service, &cached_device, &old_session_id);
if (ret < 0 || !cached_device->can_enumerate) {
continue;
}
CFRetain (libusb_darwin_acfl);
- /* add the cfSource to the aync run loop */
+ /* add the cfSource to the async run loop */
CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
}
/* device opened successfully */
dpriv->open_count++;
- usbi_dbg ("device open for access");
+ usbi_dbg (HANDLE_CTX(dev_handle), "device open for access");
return 0;
}
}
dpriv->open_count--;
+ if (NULL == dpriv->device) {
+ usbi_warn (HANDLE_CTX (dev_handle), "darwin_close device missing IOService");
+ return;
+ }
/* make sure all interfaces are released */
for (i = 0 ; i < USB_MAXINTERFACES ; i++)
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
+#if InterfaceVersion >= 550
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+ UInt8 dont_care1, dont_care3;
+ UInt16 dont_care2;
+#endif
IOReturn kresult;
UInt8 numep, direction, number;
- UInt8 dont_care1, dont_care3;
- UInt16 dont_care2;
int rc;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
- usbi_dbg ("building table of endpoints.");
+ usbi_dbg (ctx, "building table of endpoints.");
/* retrieve the total number of endpoints on this interface */
kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* iterate through pipe references */
for (UInt8 i = 1 ; i <= numep ; i++) {
+#if InterfaceVersion >= 550
+ kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties);
+ number = pipeProperties.bEndpointNumber;
+ direction = pipeProperties.bDirection;
+#else
kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
&dont_care2, &dont_care3);
-
+#endif
if (kresult != kIOReturnSuccess) {
/* probably a buggy device. try to get the endpoint address from the descriptors */
struct libusb_config_descriptor *config;
return rc;
}
+ if (iface >= config->bNumInterfaces) {
+ usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1;
cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
}
- usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
+ usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK);
}
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
/* make sure we have an interface */
if (!usbInterface && dpriv->first_config != 0) {
- usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+ usbi_info (ctx, "no interface found; setting configuration: %d", dpriv->first_config);
/* set the configuration */
ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config);
if (ret != LIBUSB_SUCCESS) {
- usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+ usbi_err (ctx, "could not set configuration");
return ret;
}
kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "darwin_get_interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
}
if (!usbInterface) {
- usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+ usbi_info (ctx, "interface not found");
return LIBUSB_ERROR_NOT_FOUND;
}
(void)IOObjectRelease (usbInterface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
if (!plugInInterface) {
- usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+ usbi_err (ctx, "plugin interface not found");
return LIBUSB_ERROR_NOT_FOUND;
}
/* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
(*plugInInterface)->Release (plugInInterface);
if (kresult != kIOReturnSuccess || !cInterface->interface) {
- usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* claim the interface */
kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
+ usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
if (ret) {
/* this should not happen */
darwin_release_interface (dev_handle, iface);
- usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ usbi_err (ctx, "could not build endpoint table");
return ret;
}
/* create async event source */
kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+ usbi_err (ctx, "could not create async event source");
/* can't continue without an async event source */
(void)darwin_release_interface (dev_handle, iface);
/* add the cfSource to the async thread's run loop */
CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
- usbi_dbg ("interface opened");
+ usbi_dbg (ctx, "interface opened");
return LIBUSB_SUCCESS;
}
if (cInterface->cfSource) {
CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
CFRelease (cInterface->cfSource);
+ cInterface->cfSource = NULL;
}
kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
return darwin_to_libusb (kresult);
}
+static int check_alt_setting_and_clear_halt(struct libusb_device_handle *dev_handle, uint8_t altsetting, struct darwin_interface *cInterface) {
+ enum libusb_error ret;
+ IOReturn kresult;
+ uint8_t current_alt_setting;
+
+ kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, ¤t_alt_setting);
+ if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) {
+ return LIBUSB_ERROR_PIPE;
+ }
+
+ for (int i = 0 ; i < cInterface->num_endpoints ; i++) {
+ ret = darwin_clear_halt(dev_handle, cInterface->endpoint_addrs[i]);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_warn(HANDLE_CTX (dev_handle), "error clearing pipe halt for endpoint %d", i);
+ if (LIBUSB_ERROR_NOT_FOUND == ret) {
+ /* may need to re-open the interface */
+ return ret;
+ }
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) {
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
IOReturn kresult;
return LIBUSB_ERROR_NO_DEVICE;
kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
- if (kresult != kIOReturnSuccess)
- darwin_reset_device (dev_handle);
+ if (kresult == kIOReturnSuccess) {
+ /* update the list of endpoints */
+ ret = get_endpoints (dev_handle, iface);
+ if (ret) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ }
+ return ret;
+ }
- /* update list of endpoints */
- ret = get_endpoints (dev_handle, iface);
- if (ret) {
- /* this should not happen */
- darwin_release_interface (dev_handle, iface);
- usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult));
+
+ ret = darwin_to_libusb(kresult);
+ if (ret != LIBUSB_ERROR_PIPE) {
return ret;
}
- return darwin_to_libusb (kresult);
+ /* If a device only supports a default setting for the specified interface, then a STALL
+ (kIOUSBPipeStalled) may be returned. Ref: USB 2.0 specs 9.4.10.
+ Mimic the behaviour in e.g. the Linux kernel: in such case, reset all endpoints
+ of the interface (as would have been done per 9.1.1.5) and return success. */
+
+ ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+ if (LIBUSB_ERROR_NOT_FOUND == ret) {
+ /* For some reason we need to reclaim the interface after the pipe error with some versions of macOS */
+ ret = darwin_claim_interface (dev_handle, iface);
+ if (LIBUSB_SUCCESS != ret) {
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not reclaim interface: %s", darwin_error_str(kresult));
+ }
+ ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+ }
+
+ return ret;
}
static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
int open_count = dpriv->open_count;
int ret;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
/* clear claimed interfaces temporarily */
dev_handle->claimed_interfaces = 0;
}
if (dpriv->active_config != active_config) {
- usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config);
+ usbi_dbg (ctx, "darwin/restore_state: restoring configuration %d...", active_config);
ret = darwin_set_configuration (dev_handle, active_config);
if (LIBUSB_SUCCESS != ret) {
- usbi_dbg ("darwin/restore_state: could not restore configuration");
+ usbi_dbg (ctx, "darwin/restore_state: could not restore configuration");
return LIBUSB_ERROR_NOT_FOUND;
}
}
- usbi_dbg ("darwin/restore_state: reclaiming interfaces");
+ usbi_dbg (ctx, "darwin/restore_state: reclaiming interfaces");
if (claimed_interfaces) {
for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) {
continue;
}
- usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface);
+ usbi_dbg (ctx, "darwin/restore_state: re-claiming interface %u", iface);
ret = darwin_claim_interface (dev_handle, iface);
if (LIBUSB_SUCCESS != ret) {
- usbi_dbg ("darwin/restore_state: could not claim interface %u", iface);
+ usbi_dbg (ctx, "darwin/restore_state: could not claim interface %u", iface);
return LIBUSB_ERROR_NOT_FOUND;
}
}
}
- usbi_dbg ("darwin/restore_state: device state restored");
+ usbi_dbg (ctx, "darwin/restore_state: device state restored");
return LIBUSB_SUCCESS;
}
-static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
+static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
int8_t active_config = dpriv->active_config;
+ UInt32 options = 0;
IOUSBDeviceDescriptor descriptor;
IOUSBConfigurationDescriptorPtr cached_configuration;
IOUSBConfigurationDescriptor *cached_configurations;
IOReturn kresult;
UInt8 i;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
if (dpriv->in_reenumerate) {
/* ack, two (or more) threads are trying to reset the device! abort! */
return LIBUSB_ERROR_NOT_FOUND;
memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
}
+ /* if we need to release capture */
+ if (HAS_CAPTURE_DEVICE()) {
+ if (capture) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
+ options |= kUSBReEnumerateCaptureDeviceMask;
+#endif
+ }
+ } else {
+ capture = false;
+ }
+
/* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
- kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
+ usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
dpriv->in_reenumerate = false;
return darwin_to_libusb (kresult);
}
- usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete...");
+ /* capture mode does not re-enumerate but it does require re-open */
+ if (capture) {
+ usbi_dbg (ctx, "darwin/reenumerate_device: restoring state...");
+ dpriv->in_reenumerate = false;
+ return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
+ }
+
+ usbi_dbg (ctx, "darwin/reenumerate_device: waiting for re-enumeration to complete...");
+
+ struct timespec start;
+ usbi_get_monotonic_time(&start);
while (dpriv->in_reenumerate) {
struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
nanosleep (&delay, NULL);
+
+ struct timespec now;
+ usbi_get_monotonic_time(&now);
+ unsigned long elapsed_us = (now.tv_sec - start.tv_sec) * USEC_PER_SEC +
+ (now.tv_nsec - start.tv_nsec) / 1000;
+
+ if (elapsed_us >= DARWIN_REENUMERATE_TIMEOUT_US) {
+ usbi_err (ctx, "darwin/reenumerate_device: timeout waiting for reenumerate");
+ dpriv->in_reenumerate = false;
+ return LIBUSB_ERROR_TIMEOUT;
+ }
}
/* compare descriptors */
- usbi_dbg ("darwin/reset_device: checking whether descriptors changed");
+ usbi_dbg (ctx, "darwin/reenumerate_device: checking whether descriptors changed");
if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) {
/* device descriptor changed. need to return not found. */
- usbi_dbg ("darwin/reset_device: device descriptor changed");
+ usbi_dbg (ctx, "darwin/reenumerate_device: device descriptor changed");
return LIBUSB_ERROR_NOT_FOUND;
}
for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
(void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
- usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i);
+ usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i);
return LIBUSB_ERROR_NOT_FOUND;
}
}
- usbi_dbg ("darwin/reset_device: device reset complete. restoring state...");
+ usbi_dbg (ctx, "darwin/reenumerate_device: device reset complete. restoring state...");
return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
}
-static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
- io_service_t usbInterface;
- CFTypeRef driver;
IOReturn kresult;
+ enum libusb_error ret;
- kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
- if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
-
- return darwin_to_libusb (kresult);
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+ if (dpriv->capture_count > 0) {
+ /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */
+ kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+ ret = darwin_to_libusb (kresult);
+ } else {
+ ret = darwin_reenumerate_device (dev_handle, false);
+ }
+#else
+ /* ResetDevice() is missing on non-macOS platforms */
+ ret = darwin_reenumerate_device (dev_handle, false);
+ if ((ret == LIBUSB_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND) && dpriv->capture_count > 0) {
+ int capture_count;
+ int8_t active_config = dpriv->active_config;
+ unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
+
+ /* save old capture_count */
+ capture_count = dpriv->capture_count;
+ /* reset capture count */
+ dpriv->capture_count = 0;
+ /* attempt to detach kernel driver again as it is now re-attached */
+ ret = darwin_detach_kernel_driver (dev_handle, 0);
+ if (ret != LIBUSB_SUCCESS) {
+ return ret;
+ }
+ /* restore capture_count */
+ dpriv->capture_count = capture_count;
+ /* restore configuration */
+ ret = darwin_restore_state (dev_handle, active_config, claimed_interfaces);
}
+#endif
+ return ret;
+}
+
+static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) {
+ CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name);
+ CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+ CFTypeRef interfaceCF = CFNumberCreate (NULL, kCFNumberSInt8Type, &interface_number);
- driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
- IOObjectRelease (usbInterface);
+ CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
+ CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
+ CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBHostMatchingPropertyInterfaceNumber), interfaceCF);
- if (driver) {
- CFRelease (driver);
+ CFRelease (interfaceCF);
+ CFRelease (locationCF);
+ CFRelease (propertyMatchDict);
+
+ return IOServiceGetMatchingService (darwin_default_master_port, matchingDict);
+}
+
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ io_service_t usb_interface, child = IO_OBJECT_NULL;
+
+ /* locate the IO registry entry for this interface */
+ usb_interface = usb_find_interface_matching_location (kIOUSBHostInterfaceClassName, interface, dpriv->location);
+ if (0 == usb_interface) {
+ /* check for the legacy class entry */
+ usb_interface = usb_find_interface_matching_location (kIOUSBInterfaceClassName, interface, dpriv->location);
+ if (0 == usb_interface) {
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+ /* if the IO object has a child entry in the IO Registry it has a kernel driver attached */
+ (void) IORegistryEntryGetChildEntry (usb_interface, kIOServicePlane, &child);
+ IOObjectRelease (usb_interface);
+ if (IO_OBJECT_NULL != child) {
+ IOObjectRelease (child);
return 1;
}
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
IOReturn kresult;
- uint8_t direction, number, interval, pipeRef, transferType;
- uint16_t maxPacketSize;
+ uint8_t pipeRef, interval;
UInt64 frame;
AbsoluteTime atTime;
int i;
+#if InterfaceVersion >= 550
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+ /* None of the values below are used in libusb for iso transfers */
+ uint8_t direction, number, transferType;
+ uint16_t maxPacketSize;
+#endif
struct darwin_interface *cInterface;
}
/* determine the properties of this endpoint and the speed of the device */
- (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+#if InterfaceVersion >= 550
+ kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
+ interval = pipeProperties.bInterval;
+#else
+ kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
&transferType, &maxPacketSize, &interval);
+#endif
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult);
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+
+ return darwin_to_libusb (kresult);
+ }
/* Last but not least we need the bus frame number */
kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
return darwin_to_libusb (kresult);
}
- (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
- &transferType, &maxPacketSize, &interval);
-
/* schedule for a frame a little in the future */
frame += 4;
uint8_t pipeRef, iface;
IOReturn kresult;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
- usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+ usbi_err (ctx, "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
if (!dpriv->device)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
+ usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef);
/* abort transactions */
#if InterfaceVersion >= 550
#endif
(*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
- usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
+ usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit");
/* newer versions of darwin support clearing additional bits on the device's endpoint */
kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
- usbi_dbg ("an async io operation has completed");
+ usbi_dbg (TRANSFER_CTX(transfer), "an async io operation has completed");
/* if requested write a zero packet */
if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
result = kIOUSBTransactionTimeout;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
switch (result) {
case kIOReturnUnderrun:
case kIOReturnSuccess:
case kIOReturnAborted:
return LIBUSB_TRANSFER_CANCELLED;
case kIOUSBPipeStalled:
- usbi_dbg ("transfer error: pipe is stalled");
+ usbi_dbg (ctx, "transfer error: pipe is stalled");
return LIBUSB_TRANSFER_STALL;
case kIOReturnOverrun:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+ usbi_warn (ctx, "transfer error: data overrun");
return LIBUSB_TRANSFER_OVERFLOW;
case kIOUSBTransactionTimeout:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ usbi_warn (ctx, "transfer error: timed out");
itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
return LIBUSB_TRANSFER_TIMED_OUT;
default:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+ usbi_warn (ctx, "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
return LIBUSB_TRANSFER_ERROR;
}
}
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
- const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"};
+ const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL};
bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
if (transfer->type > max_transfer_type) {
- usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ usbi_err (ctx, "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
if (NULL == tpriv) {
- usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv");
+ usbi_err (ctx, "malformed request is missing transfer priv");
return LIBUSB_ERROR_INVALID_PARAM;
}
- usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
+ usbi_dbg (ctx, "handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
- if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
+ if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result || kIOUSBTransactionTimeout == tpriv->result) {
if (is_isoc && tpriv->isoc_framelist) {
/* copy isochronous results back */
}
#endif
+#if InterfaceVersion >= 700
+
+/* macOS APIs for getting entitlement values */
+
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+#include <Security/Security.h>
+#else
+typedef struct __SecTask *SecTaskRef;
+extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator);
+extern CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error);
+#endif
+
+static bool darwin_has_capture_entitlements (void) {
+ SecTaskRef task;
+ CFTypeRef value;
+ bool entitled;
+
+ task = SecTaskCreateFromSelf (kCFAllocatorDefault);
+ if (task == NULL) {
+ return false;
+ }
+ value = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.vm.device-access"), NULL);
+ CFRelease (task);
+ entitled = value && (CFGetTypeID (value) == CFBooleanGetTypeID ()) && CFBooleanGetValue (value);
+ if (value) {
+ CFRelease (value);
+ }
+ return entitled;
+}
+
+static int darwin_reload_device (struct libusb_device_handle *dev_handle) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ enum libusb_error err;
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ (*(dpriv->device))->Release(dpriv->device);
+ dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service);
+ if (!dpriv->device) {
+ err = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ err = LIBUSB_SUCCESS;
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ return err;
+}
+
+/* On macOS, we capture an entire device at once, not individual interfaces. */
+
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+ UNUSED(interface);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ enum libusb_error err;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
+ if (HAS_CAPTURE_DEVICE()) {
+ } else {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ if (dpriv->capture_count == 0) {
+ usbi_dbg (ctx, "attempting to detach kernel driver from device");
+
+ if (darwin_has_capture_entitlements ()) {
+ /* request authorization */
+ kresult = IOServiceAuthorize (dpriv->service, kIOServiceInteractionAllowed);
+ if (kresult != kIOReturnSuccess) {
+ usbi_warn (ctx, "IOServiceAuthorize: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* we need start() to be called again for authorization status to refresh */
+ err = darwin_reload_device (dev_handle);
+ if (err != LIBUSB_SUCCESS) {
+ return err;
+ }
+ } else {
+ usbi_info (ctx, "no capture entitlements. may not be able to detach the kernel driver for this device");
+ if (0 != geteuid()) {
+ usbi_warn (ctx, "USB device capture requires either an entitlement (com.apple.vm.device-access) or root privilege");
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+
+ /* reset device to release existing drivers */
+ err = darwin_reenumerate_device (dev_handle, true);
+ if (err != LIBUSB_SUCCESS) {
+ return err;
+ }
+ }
+ dpriv->capture_count++;
+ return LIBUSB_SUCCESS;
+}
+
+
+static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+ UNUSED(interface);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ if (HAS_CAPTURE_DEVICE()) {
+ } else {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ dpriv->capture_count--;
+ if (dpriv->capture_count > 0) {
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg (HANDLE_CTX (dev_handle), "reenumerating device for kernel driver attach");
+
+ /* reset device to attach kernel drivers */
+ return darwin_reenumerate_device (dev_handle, false);
+}
+
+static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+ enum libusb_error ret;
+ if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) {
+ ret = darwin_detach_kernel_driver (dev_handle, iface);
+ if (ret != LIBUSB_SUCCESS) {
+ usbi_info (HANDLE_CTX (dev_handle), "failed to auto-detach the kernel driver for this device, ret=%d", ret);
+ }
+ }
+
+ return darwin_claim_interface (dev_handle, iface);
+}
+
+static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+ enum libusb_error ret;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ ret = darwin_release_interface (dev_handle, iface);
+ if (ret != LIBUSB_SUCCESS) {
+ return ret;
+ }
+
+ if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) {
+ ret = darwin_attach_kernel_driver (dev_handle, iface);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret);
+ }
+ /* ignore the error as the interface was successfully released */
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+#endif
+
const struct usbi_os_backend usbi_backend = {
.name = "Darwin",
- .caps = 0,
+ .caps = USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
.init = darwin_init,
.exit = darwin_exit,
.get_active_config_descriptor = darwin_get_active_config_descriptor,
.close = darwin_close,
.get_configuration = darwin_get_configuration,
.set_configuration = darwin_set_configuration,
- .claim_interface = darwin_claim_interface,
- .release_interface = darwin_release_interface,
.set_interface_altsetting = darwin_set_interface_altsetting,
.clear_halt = darwin_clear_halt,
.kernel_driver_active = darwin_kernel_driver_active,
+#if InterfaceVersion >= 700
+ .detach_kernel_driver = darwin_detach_kernel_driver,
+ .attach_kernel_driver = darwin_attach_kernel_driver,
+ .claim_interface = darwin_capture_claim_interface,
+ .release_interface = darwin_capture_release_interface,
+#else
+ .claim_interface = darwin_claim_interface,
+ .release_interface = darwin_release_interface,
+#endif
+
.destroy_device = darwin_destroy_device,
.submit_transfer = darwin_submit_transfer,
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
+#if defined(HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H)
+#include <IOKit/usb/IOUSBHostFamilyDefinitions.h>
+#endif
+
/* IOUSBInterfaceInferface */
/* New in OS 10.12.0. */
-#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
+#if defined (kIOUSBInterfaceInterfaceID800)
#define usb_interface_t IOUSBInterfaceInterface800
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
#define InterfaceVersion 800
/* New in OS 10.10.0. */
-#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000)
+#elif defined (kIOUSBInterfaceInterfaceID700)
#define usb_interface_t IOUSBInterfaceInterface700
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
#define InterfaceVersion 700
/* New in OS 10.9.0. */
-#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID650)
#define usb_interface_t IOUSBInterfaceInterface650
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
#define InterfaceVersion 650
/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID550)
#define usb_interface_t IOUSBInterfaceInterface550
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
#define InterfaceVersion 550
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBInterfaceInterfaceID500)
#define usb_interface_t IOUSBInterfaceInterface500
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
#define InterfaceVersion 500
/* New in OS 10.5.0. */
-#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID300)
#define usb_interface_t IOUSBInterfaceInterface300
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
#define InterfaceVersion 300
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID245)
#define usb_interface_t IOUSBInterfaceInterface245
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
#define InterfaceVersion 245
/* New in OS 10.4.0. */
-#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040)
+#elif defined (kIOUSBInterfaceInterfaceID220)
#define usb_interface_t IOUSBInterfaceInterface220
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
/* IOUSBDeviceInterface */
/* New in OS 10.9.0. */
-#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#if defined (kIOUSBDeviceInterfaceID650)
#define usb_device_t IOUSBDeviceInterface650
#define DeviceInterfaceID kIOUSBDeviceInterfaceID650
#define DeviceVersion 650
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBDeviceInterfaceID500)
#define usb_device_t IOUSBDeviceInterface500
#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
#define DeviceVersion 500
/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
+#elif defined (kIOUSBDeviceInterfaceID320)
#define usb_device_t IOUSBDeviceInterface320
#define DeviceInterfaceID kIOUSBDeviceInterfaceID320
#define DeviceVersion 320
/* New in OS 10.5.0. */
-#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID300)
#define usb_device_t IOUSBDeviceInterface300
#define DeviceInterfaceID kIOUSBDeviceInterfaceID300
#define DeviceVersion 300
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID245)
#define usb_device_t IOUSBDeviceInterface245
#define DeviceInterfaceID kIOUSBDeviceInterfaceID245
#define DeviceVersion 245
/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030)
+#elif defined (kIOUSBDeviceInterfaceID197)
#define usb_device_t IOUSBDeviceInterface197
#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
#endif
+#if !defined(kIOUSBHostInterfaceClassName)
+#define kIOUSBHostInterfaceClassName "IOUSBHostInterface"
+#endif
+
+#if !defined(kUSBHostMatchingPropertyInterfaceNumber)
+#define kUSBHostMatchingPropertyInterfaceNumber "bInterfaceNumber"
+#endif
+
#if !defined(IO_OBJECT_NULL)
#define IO_OBJECT_NULL ((io_object_t) 0)
#endif
+/* Testing availability */
+#ifndef __has_builtin
+ #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
+#endif
+#if __has_builtin(__builtin_available)
+ #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *)
+#else
+ #define HAS_CAPTURE_DEVICE() 0
+#endif
+
typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
typedef IONotificationPortRef io_notification_port_t;
USBDeviceAddress address;
char sys_path[21];
usb_device_t **device;
+ io_service_t service;
int open_count;
UInt8 first_config, active_config, port;
int can_enumerate;
int refcount;
bool in_reenumerate;
+ int capture_count;
};
struct darwin_device_priv {
usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
int internal_fds, num_ready;
- usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+ usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
num_ready = poll(fds, nfds, timeout_ms);
- usbi_dbg("poll() returned %d", num_ready);
+ usbi_dbg(ctx, "poll() returned %d", num_ready);
if (num_ready == 0) {
if (usbi_using_timer(ctx))
goto done;
continue;
/* pollfd was removed between the creation of the fds array and
* here. remove triggered revent as it is no longer relevant. */
- usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+ usbi_dbg(ctx, "fd %d was removed, ignoring raised events", fds[n].fd);
fds[n].revents = 0;
num_ready--;
break;
{
int *completed = transfer->user_data;
*completed = 1;
- usbi_dbg("actual_length=%d", transfer->actual_length);
+ usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length);
/* caller interprets result and frees transfer */
}
#define LIBUSB_MINOR 0
#endif
#ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 24
+#define LIBUSB_MICRO 26
#endif
#ifndef LIBUSB_NANO
#define LIBUSB_NANO 0
-#define LIBUSB_NANO 11584
+#define LIBUSB_NANO 11724